package com.epcs.model.bo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.epcs.model.dao.EventDAO;
import com.epcs.model.dao.UserDAO;
import com.epcs.model.dao.googleWrapper.GoogleAPIWrapper;
import com.epcs.model.dao.outlookWrapper.OutlookAPIWrapper;
import com.epcs.model.dto.Account;
import com.epcs.model.dto.Email;
import com.epcs.model.dto.Event;
import com.epcs.model.dto.android.AndroidEvent;
import com.epcs.model.dto.android.AndroidEventList;
import com.epcs.model.dto.google.GoogleCalendar;
import com.epcs.model.dto.google.GoogleDateTime;
import com.epcs.model.dto.google.GoogleEvent;
import com.epcs.model.dto.google.GoogleReminder;
import com.epcs.model.dto.google.Overrides;
import com.epcs.model.dto.outlook.OutlookCalendar;
import com.epcs.model.dto.outlook.OutlookEvent;
import com.epcs.util.EpcsDateTimeUtil;

public class SynchronizationBO {

	/**
	 * 
	 * @param account
	 * @return
	 */
	public int checkEmailBeforeSync(Account account) {
		// get Email list để chủ yếu là lấy refresh token
		EmailBO emailBO = new EmailBO();
		List<Email> emailList = emailBO.getAllEmail(account);

		// kiểm tra xem đã xác nhận email của lịch để đồng bộ chua
		if (emailList != null) {
			// nếu đã xác nhận email của lịch để đồng bộ, xử lý đồng bộ

			// xem xét email đã có calendar ID chưa, chưa có thì chuyển sang trang chọn
			boolean noNeedToGetCalendarID = true;
			for (int i = 0; i < emailList.size() && noNeedToGetCalendarID; i++) {
				if (emailList.get(i).getEpcsCalendarID() == null) {
					noNeedToGetCalendarID = false;
				}
			}
			if (noNeedToGetCalendarID) {
				// email đáp ứng đủ yuêu cầu trước khi sync
				// trả về xác nhận ok
				return 1;
			} else {

				return -2;
				// tức là có email chưa chọn calendar để đồng bộ
				// yêu cầu việc chọn calendar từ phía user

				/**
				 * Khi đó chưa có bất kỳ calendar nào Vì vậy ko bit phải chọn calendar nào để đồng
				 * bộ vì vậy phải chuyển sang method khác, lấy tất cả calendar name về cho user chọn
				 */
			}

		} else {
			return -1;// chua có đăng ký email nào
		}
	}
	
	public synchronized int synchronize(Account account) {
		
		// get Email list để chủ yếu là lấy refresh token
		EmailBO emailBO = new EmailBO();
		List<Email> emailList = emailBO.getAllEmail(account);

		boolean result = executeSync(account, emailList);
		if (!result) {
			return 0;
		}
		
		
		return 1; // thành công
	}

	/**
	 * 
	 * @param account
	 *            is Account type
	 * @return Map. Key: is the calendar type (GOOGLE_CALENDAR, OUTLOOK_CALENDAR,...). Value: is the
	 *         Map data type with (Email, List). Email is the email object; List is the List of
	 *         calendar that has been chosen by user
	 * 
	 */
	public synchronized Map<String, Map<String, List>> getAllCalendarForChoosing(Account account) {
		// lấy tất cả các email vẫn chưa có chọn calendar ID
		Map<String, Map<String, List>> map = null;
		Map<String, List> listOfGoogleCalendarWithEmail = null;
		Map<String, List> listOfOutlookCalendarWithEmail = null;
		
		// lấy email list
		EmailBO emailBO = new EmailBO();
		List<Email> emailList = emailBO.getAllEmail(account);

		if (emailList != null) {
			// kiểm tra xem đã xác nhận email của lịch để đồng bộ chua
			if (emailList.size() > 0) {
				map = new HashMap<String, Map<String, List>>();
				listOfGoogleCalendarWithEmail = new HashMap<String, List>();
				listOfOutlookCalendarWithEmail = new HashMap<String, List>();

				// -------- thực hiện lấy data từ server về ----------------//
				// thục hiện với từng email ( chú ý: email nào chưa có calendarID thì mới sử dụng)
				for (int i = 0; i < emailList.size(); i++) {
					Email item = emailList.get(i);
					if (item != null && item.getEpcsCalendarID() == null) {
						if (item.getEmail().endsWith("@gmail.com")) {
							// khoi tao wrapper
							GoogleAPIWrapper googleWrapper = new GoogleAPIWrapper(item.getRefreshToken());
							// lấy calendar List về
							List<GoogleCalendar> calendarList = googleWrapper.getCalendarList();

							// đưa vào trong map
							listOfGoogleCalendarWithEmail.put(item.getEmail(), calendarList);

						} else if (item.getEmail().endsWith("@outlook.com") || item.getEmail().endsWith("@hotmail.com")
								|| item.getEmail().endsWith("@live.com")) {
							// khoi tao wrapper
							OutlookAPIWrapper outlookWrapper = new OutlookAPIWrapper(item.getRefreshToken());
							// lấy calendar List về
							List<OutlookCalendar> calendarList = outlookWrapper.getCalendarList();

							// đưa vào trong map
							listOfOutlookCalendarWithEmail.put(item.getEmail(), calendarList);
						}
					}
				}

				// phân loại theo calendar type (GOOGLE_CALENDAR và
				// OUTLOOK_CALENDAR)
				

				/**
				 * Trường hợp cần validate
				 * 
				 * Mục đích của method là phân loại ra 2 loại service: google, outlook
				 * Param vào thể hiện 2 trường hợp: 
				 * 1. không muốn lấy calendar ID nữa (tức là trong param truyền ko có chuỗi phân loại)
				 * 2. muốn lấy calendar ID ( cì trong db chưa có)
				 * 
				 * Và kết quả sẽ có các trường hợp:
				 * 1. listOfGoogleCalendarWithEmail ko có phần tử nào (do ko muốn lấy)
				 * 2. listOfGoogleCalendarWithEmail có phần tử mà calendarList null(do ko lấy được)
				 * 
				 * vậy nên phải chặn kết quả trước cho trang jsp xử lý
				 */
				if (listOfGoogleCalendarWithEmail.size() > 0){
					// xét nó có muốn lấy calendarID hay ko. Nếu có ý định lấy thì nhét vào
					// jsp sẽ xử lý việc ko lấy dc kết quả và báo cho user
					map.put("GOOGLE_CALENDAR", listOfGoogleCalendarWithEmail);
				}
				
				if (listOfOutlookCalendarWithEmail.size() > 0){
					// xét nó có muốn lấy calendarID hay ko. Nếu có ý định lấy thì nhét vào
					// jsp sẽ xử lý việc ko lấy dc kết quả và báo cho user
					map.put("OUTLOOK_CALENDAR", listOfOutlookCalendarWithEmail);
				}
				
			}
		}

		return map;
	}

	/**
	 * method thực hiện lấy tất cả event sau khi user đã chọn dc các calendar cần thiết (còn hơi
	 * rối)
	 * 
	 * @param account
	 * @param calendarMap
	 * @return
	 */
	public synchronized boolean synchronizeFortheFirstTime(Account account, Map<String, Map<Email, List<String>>> calendarMap) {

		EventBO eventBO = new EventBO();
		try {
			// List các event sau khi đã lấy từ các server về
			List<Event> systemGoogleEventList = new ArrayList<Event>();
			List<Event> systemOutlookEventList = new ArrayList<Event>();
			List<Event> systemNewestEventList = eventBO.getAllEventForSync(account);

			/**
			 * lần đầu sync nên systemNewestEventList chỉ có những event status là confirm 
			 * mới dc sync. cancelled thì không cần sync
			 */
			for (int i = 0; i < systemNewestEventList.size(); i++){
				Event systemEvent = systemNewestEventList.get(i);
				
				if (systemEvent.getStatus().equals("cancelled")){
					systemNewestEventList.remove(i);

					// lùi lại 1 đơn vị để duyệt tiếp
					i = i - 1; 
				}
			}
			
			/*
			 * lấy các calendar user đã chọn, phân loại nó ra các map (GOOGLE_CALENDAR,
			 * OUTLOOK_CALENDAR,...)
			 */

			// --------------- tác vụ lấy event thuộc các calendar
			/*-----------------------GOOGLE_CALENDAR---------------------------*/
			/*
			 * lấy list các email ra, để làm key dùng để lấy list các calendar user đã chọn tương
			 * ứng với từ email
			 */
			System.out.println("Google ---");
			Map<Email, List<String>> mapOfGoogleCalendarWithEmail = calendarMap.get("GOOGLE_CALENDAR");
			Set<Email> googleEmailSet = mapOfGoogleCalendarWithEmail.keySet();
			List<Email> googleEmailList = new ArrayList<Email>(googleEmailSet);
			// nơi lưu trữ cho cả GoogleAPI và OutlookAPI de sử dụng lâu dài
			Map<String, Object> apiMap = new HashMap<String, Object>();

			// lấy api về sẵn, phân loại sẵn cả email
			for (int i = 0; i < googleEmailList.size(); i++) {
				Email email = googleEmailList.get(i);

				// khoi tao wrapper
				GoogleAPIWrapper api = new GoogleAPIWrapper(email.getRefreshToken());
				apiMap.put(email.getEmail(), api);
			}

			// Cải tiến ở đây (thêm email trong event để hỗ trợ nhiều acc gmail)---//
			// sau khi có email list... thì với mỗi email trong list...
			for (int i = 0; i < googleEmailList.size(); i++) {
				// mới chỉ hỗ trợ 1 acc gmail--> i=0 --> đưa luôn calendarID vào epceCalendarID
				// trong email
				Email email = googleEmailList.get(i);
				// lấy các list calendar tương ứng với từng email, thông qua map
				List<String> listOfCalendarID = mapOfGoogleCalendarWithEmail.get(email);
				// thiết lập api
				GoogleAPIWrapper api = (GoogleAPIWrapper) (apiMap.get(email.getEmail()));
				// với mỗi calendar đó, lấy list các google event tương ứng
				List<GoogleEvent> eventList = new ArrayList<GoogleEvent>();

				// ---------- Lấy list các google event tương ứng---------//
				for (int j = 0; j < listOfCalendarID.size(); j++) {

					String calendarID = listOfCalendarID.get(j);
					getGoogleEventList(calendarID, api, eventList);

					/*
					 * Thành phần ngoại lai: Chỗ này là vì hệ thống mới hỗ trợ 1 acc tương ứng với 1
					 * calendarID, nên tạo ghi calendarID vào trong email, thay cho epcsCalendarID
					 */
					email.setEpcsCalendarID(calendarID);
					// ghi vào db
					EmailBO bo = new EmailBO();
					bo.updateEmail(email);

				}
				// ---------- Kết thúc Lấy list các google event tương ứng---------//

				/*
				 * ------------------Parse từ google event sang system event --------------
				 * 
				 * Sau khi đã có list các event từ google calendar, parse sang system event list,
				 * đưa các event đã parse vao systemGoogleEventList để đưa lên outlook
				 */

				for (int x = 0; x < eventList.size(); x++) {
					GoogleEvent googleEvent = eventList.get(x);
					Event systemEvent = convertFromGoogleEventToSystemEvent(account, googleEvent);
					systemEvent.setDirty(true);

					systemGoogleEventList.add(systemEvent);
				}
				// ---------- Kết thúc parse từ google event sang system event---------//

			}

			/*-----------------------OUTLOOK_CALENDAR---------------------------*/
			/*
			 * lấy list các email ra, để làm key dùng để lấy list các calendar user đã chọn tương
			 * ứng với từ email
			 */
			System.out.println("Outlook ---");

			Map<Email, List<String>> mapOfOutlookCalendarWithEmail = calendarMap.get("OUTLOOK_CALENDAR");
			Set<Email> outlookEmailSet = mapOfOutlookCalendarWithEmail.keySet();
			List<Email> outlookEmailList = new ArrayList<Email>(outlookEmailSet);

			// lấy api về sẵn, phân loại sẵn cả email
			for (int i = 0; i < outlookEmailList.size(); i++) {
				Email email = outlookEmailList.get(i);

				// khoi tao wrapper
				OutlookAPIWrapper api = new OutlookAPIWrapper(email.getRefreshToken());
				apiMap.put(email.getEmail(), api);
			}

			// Cải tiến ở đây (thêm email trong event để hỗ trợ nhiều acc outlook)---//
			// sau khi có email list... thì với mỗi email trong list...
			for (int i = 0; i < outlookEmailList.size(); i++) {// mới hỗ trợ 1 acc
																// outlook--> i=0
				Email email = outlookEmailList.get(i);

				// lấy các list calendar tương ứng với từng email, thông qua map
				List<String> listOfCalendarID = mapOfOutlookCalendarWithEmail.get(email);
				// thiết lập api
				OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
				// với mỗi calendar đó, lấy list các outlook event tương ứng
				List<OutlookEvent> eventList = new ArrayList<OutlookEvent>();

				// ---------- Lấy list các outlook event tương ứng---------//
				for (int j = 0; j < listOfCalendarID.size(); j++) {

					String calendarID = listOfCalendarID.get(j);
					getOutlookEventList(calendarID, api, eventList);

					/*
					 * Thành phần ngoại lai: Chỗ này là vì hệ thống mới hỗ trợ 1 acc tương ứng với 1
					 * calendarID, nên ta sẽ ghi calendarID vào trong email, thay cho epcsCalendarID
					 */
					email.setEpcsCalendarID(calendarID);
					// ghi vào db
					EmailBO bo = new EmailBO();
					bo.updateEmail(email);

				}
				// ---------- Kết thúc Lấy list các outlook event tương ứng---------//

				/*
				 * ------------------Parse từ outlook event sang system event --------------
				 * 
				 * Sau khi đã có list các event từ outlook calendar, parse sang system event list,
				 * đưa các event đã parse vao systemOutlookEventList để đưa lên google
				 */
				for (int x = 0; x < eventList.size(); x++) {
					OutlookEvent outlookEvent = eventList.get(x);
					Event systemEvent = convertFromOutlookEventToSystemEvent(account, outlookEvent);
					systemEvent.setDirty(true);
					
					systemOutlookEventList.add(systemEvent);
				}
				// ---------- Kết thúc parse từ outlook event sang system event ---------//
			}

			/**
			 * Sau khi có được systemOutlookEventList và systemGoogleEventList Đưa của cái này lên
			 * service của cái kia
			 */

			// ---------------------chuyển từ dạng system sang dạng google -----------//
			// -------------------Thực hiện trao đổi data outlook với google-----------//
			commitNewOutlookEventsToGoogleServices(systemOutlookEventList, googleEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng google -----------//

			// -------------- chuyển từ dạng system sang dạng outlook -----------//
			// --------------Thực hiện trao đổi data google với outlook-----------//
			commitNewGoogleEventsToOutlookServices(systemGoogleEventList, outlookEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng outlook -----------//

			// sau khi trao đổi data giữa các server, update lại data cho server chủ
			// --------Google-------//
			for (int i = 0; i < googleEmailList.size(); i++) {
				Email email = googleEmailList.get(i);
				// thiết lập api
				GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());

				for (int j = 0; j < systemGoogleEventList.size(); j++) {

					commitAgainToOwnerService(j, systemGoogleEventList, api);
				}
			}

			// --------Outlook-------//
			for (int i = 0; i < outlookEmailList.size(); i++) {
				Email email = outlookEmailList.get(i);
				// thiết lập api
				OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());

				for (int j = 0; j < systemOutlookEventList.size(); j++) {

					commitAgainToOwnerService(j, systemOutlookEventList, api);
				}
			}

			// ----------Cuối cùng là GHI VAO DB --------cho can sua//

			Set<Event> eventSet = new HashSet<Event>();
			eventSet.addAll(systemGoogleEventList);
			eventSet.addAll(systemOutlookEventList);

			String last_sync = EpcsDateTimeUtil.getCurrentIsoDateTime();
			account.setEvents(eventSet);
			account.setLastupdated(last_sync);
			UserDAO dao = new UserDAO();
			dao.updateAccount(account);

			// tạo following cho các event đó
			FollowingBO followingBO = new FollowingBO();
			for (int i = 0; i < systemGoogleEventList.size(); i++) {
				Event event = systemGoogleEventList.get(i);
				followingBO.followingEventOrCategory(event, account);
			}

			for (int i = 0; i < systemOutlookEventList.size(); i++) {
				Event event = systemOutlookEventList.get(i);
				followingBO.followingEventOrCategory(event, account);
			}
			// ---------- Kết thúc GHI VAO DB --------/

			
			//--------- đồng bộ cho events dc tạo trên system trước khi đồng bộ lần đầu---//
			// đồng bộ event mới (từ system) --> tạo mới ở các nơi và update lại ở service chủ

			// ---------------------chuyển từ dạng system sang dạng google -----------//
			// -------------------Thực hiện trao đổi data outlook với google-----------//
			commitNewOutlookEventsToGoogleServices(systemNewestEventList, googleEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng google -----------//

			// -------------- chuyển từ dạng system sang dạng outlook -----------//
			// --------------Thực hiện trao đổi data google với outlook-----------//
			commitNewGoogleEventsToOutlookServices(systemNewestEventList, outlookEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng outlook -----------//

			// sau khi trao đổi data giữa các server, update lại data cho server chủ

			// ----------Cuối cùng là GHI VAO DB --------//

			for (int i = 0; i < systemNewestEventList.size(); i++) {
				Event event = systemNewestEventList.get(i);

				eventBO.updateEvent(event);
			}

			last_sync = EpcsDateTimeUtil.getCurrentIsoDateTime();
			account.setLastupdated(last_sync);
			(new UserDAO()).updateAccount(account);
			// ---------- Kết thúc GHI VAO DB --------/
			// Kết Thúc --- đồng bộ event mới (từ external service)
			
			
			return true;
		} catch (Exception ex) {
			System.out.println("ERROR: " + ex.getMessage());
			ex.printStackTrace();
		}
		// --------------------- Kết Quả Trả Về nếu Lỗi--------------------//
		return false;
	}

	/**
	 * Xử lý đồng bộ như bình thường
	 * 
	 * @param account
	 * @param emailList
	 * @return
	 */
	private synchronized boolean executeSync(Account account, List<Email> emailList) {
		List<Event> systemEventListFromGoogle = new ArrayList<Event>();
		List<Event> systemEventListFromOutlook = new ArrayList<Event>();
		Map<Email, List<GoogleEvent>> mapOfGoogleEventWithEmail = new HashMap<Email, List<GoogleEvent>>();
		Map<Email, List<OutlookEvent>> mapOfOutlookEventWithEmail = new HashMap<Email, List<OutlookEvent>>();
		Map<String, Object> apiMap = new HashMap<String, Object>();
		List<Email> googleEmailList = new ArrayList<Email>();
		List<Email> outlookEmailList = new ArrayList<Email>();

		try {
			// lấy api về sẵn, phân loại sẵn cả email
			for (int i = 0; i < emailList.size(); i++) {
				Email email = emailList.get(i);
				if (email.getEmail().endsWith("@gmail.com")) {
					// khoi tao wrapper
					GoogleAPIWrapper api = new GoogleAPIWrapper(email.getRefreshToken());
					apiMap.put(email.getEmail(), api);
					googleEmailList.add(email);

				} else if (email.getEmail().endsWith("@outlook.com") || email.getEmail().endsWith("@hotmail.com")
						|| email.getEmail().endsWith("@live.com")) {
					// khoi tao wrapper
					OutlookAPIWrapper api = new OutlookAPIWrapper(email.getRefreshToken());
					apiMap.put(email.getEmail(), api);
					outlookEmailList.add(email);
				}
			}

			// ----------lấy data từ server về -----------//
			// -----Google
			for (int i = 0; i < googleEmailList.size(); i++) {
				Email email = googleEmailList.get(i);
				if (email != null) {
					// calendarID
					String calendarId = email.getEpcsCalendarID();

					// // khoi tao wrapper
					// GoogleAPIWrapper api = new GoogleAPIWrapper(email.getRefreshToken());
					// get api từ apiMap
					GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());
					// lấy event List về , dựa theo method có sẵn trong Bo
					List<GoogleEvent> googleEventList = new ArrayList<GoogleEvent>();
					getGoogleEventList(calendarId, api, googleEventList);

					// đưa vào trong map
					mapOfGoogleEventWithEmail.put(email, googleEventList);
				}
			}
			// ----- Outlook
			for (int i = 0; i < outlookEmailList.size(); i++) {
				Email email = outlookEmailList.get(i);
				if (email != null) {
					// calendarID
					String calendarId = email.getEpcsCalendarID();

					// // khoi tao wrapper
					// OutlookAPIWrapper api = new OutlookAPIWrapper(email.getRefreshToken());
					// get api từ apiMap
					OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
					// lấy event List về , dựa theo method có sẵn trong Bo
					List<OutlookEvent> outlookEventList = new ArrayList<OutlookEvent>();
					getOutlookEventList(calendarId, api, outlookEventList);

					// đưa vào trong map
					mapOfOutlookEventWithEmail.put(email, outlookEventList);
				}
			}
			// ----------Hoàn tất lấy data từ server về -----------//

			/**
			 * tạo ta system event : gọi tắt là sys1
			 */
			// ------chuyển đổi các external event thành system event----//
			// lấy từng email trong list làm key lục trong từng map, nếu trùng thì lấy ra xử lý
			for (int i = 0; i < emailList.size(); i++) {
				Email email = emailList.get(i);

				if (mapOfGoogleEventWithEmail.containsKey(email)) {
					// ------chuyển đổi google event thành system event----//

					// lấy list các event ra
					List<GoogleEvent> googleEventList = mapOfGoogleEventWithEmail.get(email);

					// với mỗi event của google, chuyển thành system event
					for (int j = 0; j < googleEventList.size(); j++) {
						GoogleEvent event = googleEventList.get(j);

						// chuyển thành system event
						Event systemEvent = convertFromGoogleEventToSystemEvent(account, event);

						// add vào system list
						systemEventListFromGoogle.add(systemEvent);
					}

					// ------kết thúc chuyển đổi google event thành system event----//
				} else if (mapOfOutlookEventWithEmail.containsKey(email)) {
					// ------chuyển đổi outlook event thành system event----//

					// lấy list các event ra
					List<OutlookEvent> outlookEventList = mapOfOutlookEventWithEmail.get(email);
					// với mỗi event của outlook, chuyển thành system event
					for (int j = 0; j < outlookEventList.size(); j++) {
						OutlookEvent event = outlookEventList.get(j);

						// chuyển thành system event
						Event systemEvent = convertFromOutlookEventToSystemEvent(account, event);

						// add vào system list
						systemEventListFromOutlook.add(systemEvent);
					}

					// ------kết thúc chuyển đổi outlook event thành system event----//
				} else {

				}

			}

			// lấy system event trong db lên: gọi tắt là sys2
			EventBO eventBO = new EventBO();
			List<Event> systemEventListFromDB = eventBO.getAllEventForSync(account);

			/*
			 * Lý giải: ban đầu, khi hệ thống chưa có event bắt đầu đồng bộ. Thực thi insert event
			 * của service này sang service kia nếu sau khi insert mà cột event ID dành cho service
			 * đó là null thì sẽ hủy object trong bộ nhớ, ko thể lưu vào DB Vì vậy kết quả đồng bộ
			 * luôn là nhg event có 2 event ID trên 2 service.
			 * 
			 * Từ lần đồng bộ thứ 2: hệ thống chỉ có thể có 2 loại event là event CÓ tồn tại cả 2
			 * event ID cho external service (update, delete) event KO tồn tại cả 2 event ID cho
			 * external service (new)
			 * 
			 * 
			 * phân loại event (mới, update, delete): Mới tức là sys có event mà ko có cả 2 event
			 * ID, hoặc service đã có mà sys chưa có Delete là service ko có (phải hỏi), hoặc sys có
			 * status = cancelled Update là sequence hoặc updated bị thay đổi
			 * 
			 * Về Update event đối với google thì * updated luôn thay đổi khi trên server có gì đó
			 * thay đổi sequence bị thay đổi chỉ khi thay đổi thời gian, status, rrlue --> kiểm tra
			 * sequence trước, nếu ko có change thì kiểm tra updated
			 * 
			 * đối với outlook thì luôn thay đổi updated_time khi có sự thay đổi --> chỉ cần kiểm
			 * tra updated_time
			 * 
			 * lưu ý: kiểm ta updated_time hay updated thì phải chuyển sang số Long
			 * ------------------
			 * ---------------------------------------------------------------------- có các loại
			 * event hệ thống cần xử lý
			 * 
			 * G1 1S1 1O --> update | G1 1S1 0O --> chỉ có tại google và sys | G0 1S1 1O --> chỉ có
			 * tại outlook và sys | G1 0S0 0O --> chỉ có tại google --> new trên external | G0 0S0
			 * 1O --> chỉ có tại outlook --> new trên external | G0 0S0 0O --> mới tạo tại sys -->
			 * new |
			 * 
			 * cách duyệt tốt nhất là đọc google sau đó so với system và outlook, sau đó nếu có thì
			 * lấy outlook và làm ngược lại. Trong lần đầu tiên chạy sẽ lấy hết 1111 --> ko cần lấy
			 * 1111 trong lần 2 chạy lần 1 lọc ra dc các google mới 1000 (cần tạo trên system) chạy
			 * lần 1 là lấy dc các event chỉ có trên google và system 1110
			 * 
			 * chạy lần 2 thì lấy hết dc event mới trên outlook 0001 --> tạo mới trên hệ thống chạy
			 * lần 2 sẽ lấy dc hết các event chỉ tồn tại trên outlook và system 0111
			 * 
			 * còn lại trong system là những event chắc chắn new --> cần tạo trên google và outlook
			 */

			// lọc lấy các event nào mới --> chưa có
			// dùng để lưu event dc tạo mới trên system
			List<Event> systemNewestEventList = new ArrayList<Event>();
			// dùng để lưu event dc tạo mới trên service
			List<Event> serviceNewestEventList = new ArrayList<Event>();
			// dùng để lưu event cần được update
			List<Event> googleAndSystemEventList = new ArrayList<Event>();
			// dùng để lưu event cần được update
			List<Event> outlookAndSystemEventList = new ArrayList<Event>();
			// dùng để lưu event cần được update
			List<Event> updatedEventList = new ArrayList<Event>();

			// -----------------------Bắt Đầu Duyệt----------------------//

			// duyệt từng google event
			for (int i = 0; i < systemEventListFromGoogle.size(); i++) {
				Event googleEvent = systemEventListFromGoogle.get(i);

				boolean nextForSystemList = true;
				if (googleEvent != null) {
					// duyệt từng system event để xem có tồn tại hay ko
					for (int j = 0; j < systemEventListFromDB.size() && nextForSystemList; j++) {
						Event systemEvent = systemEventListFromDB.get(j);

						if (systemEvent != null) {
							// nếu có tồn tại cặp G-S
							if (googleEvent.getGoogleEventId().equals(systemEvent.getGoogleEventId())) {
								// xét tip coi có tồn tại cặp S-O ko
								// duyệt outlook event
								boolean nextForOutlookList = true;
								for (int z = 0; z < systemEventListFromOutlook.size() && nextForOutlookList; z++) {
									Event outlookEvent = systemEventListFromOutlook.get(z);

									if (outlookEvent != null) {
										// nếu tồn tại cặp S-O
										if (systemEvent.getOutlookEventId().equals(outlookEvent.getOutlookEventId())) {
											// đó chính là event G-S-O cần update

											// phải xác định là cancelled hay là update--> ưu tiên
											// cancelled trước
											// cancelled là trạng thái event trên hệ thống (người
											// dùng muốn delete)
											if (systemEvent.getStatus().toLowerCase().equals("cancelled")) {
												
												// nhét vào updatedEventList
												updatedEventList.add(systemEvent);
											} else {
												// thực hiện so sánh xem cái nào là cái mới nhất
												Event temp = EventBO.getNewestEventBetweenPairOfSystemEvent(
														systemEvent, googleEvent);
												Event updatedEvent = EventBO.getNewestEventBetweenPairOfSystemEvent(
														temp, outlookEvent);
												
												// nhét vào updatedEventList
												updatedEventList.add(updatedEvent);
											}

											// xóa event tương ứng trong google list, system list và
											// outlook list
											systemEventListFromOutlook.remove(z);
											systemEventListFromDB.remove(j);
											systemEventListFromGoogle.remove(i);

											// ngưng tìm kiếm trong system và outlook dựa trên
											// google event này (đã xử lý)
											nextForOutlookList = false;
											nextForSystemList = false;
										}
									}
									// nếu ko tồn tại thì lấy outlook tip theo cho tới hết list thì
									// thôi
								}

								// thoát khỏi vòng lặp tìm outlook --> nextForOutlookList = true
								// tức là chưa tìm ra --> ko có trong list
								// loại event 1110. Đưa vào googleAndSystemEventList
								
								// chú ý: có thể user chỉ mới đăng ký 1 acc google, chưa có outlook
								// chỉ thực hiện phân loại googleAndSystemEventList khi và chỉ khi
								// có email outlook. Nếu ko có email outlook--> dạng update
								if (outlookEmailList.size() != 0){
									// tức là có đăng ký email outlook
									if (nextForOutlookList) {
										// đưa vào googleAndSystemEventList
										Event temp = EventBO.getNewestEventBetweenPairOfSystemEvent(systemEvent,
												googleEvent);
										if (temp != null) {
											googleAndSystemEventList.add(temp);
										} else {
											googleAndSystemEventList.add(systemEvent);
										}
										// đưa vào thì phải xóa đi trong systemEventListFromGoogle,
										// systemEventListFromDB
										systemEventListFromGoogle.remove(i);
										systemEventListFromDB.remove(j);

										// ngừng tìm kiếm system
										nextForSystemList = false;
									}
								} else {
									// nếu ko đăng ký outlook thì xem nó là update event
									
									// phải xác định là cancelled hay là update--> ưu tiên
									// cancelled trước
									// cancelled là trạng thái event trên hệ thống (người
									// dùng muốn delete)
									if (systemEvent.getStatus().toLowerCase().equals("cancelled")) {
										// nhét vào updatedEventList
										updatedEventList.add(systemEvent);
									} else {
										// thực hiện so sánh xem cái nào là cái mới nhất
										Event updatedEvent = EventBO.getNewestEventBetweenPairOfSystemEvent(systemEvent, googleEvent);
										// nhét vào updatedEventList
										updatedEventList.add(updatedEvent);
									}

									// xóa event tương ứng trong google list, system list và
									// outlook list
									systemEventListFromDB.remove(j);
									systemEventListFromGoogle.remove(i);

									// ngưng tìm kiếm trong system và outlook dựa trên
									// google event này (đã xử lý)
									nextForOutlookList = false;
									nextForSystemList = false;
								}
								
							}
						}

						// vẫn chưa tìm dc system event tương ứng thì tìm tới hết list thì thôi
					}

					if (nextForSystemList) {
						// đã duyệt hết system list mà vẫn ko thấy tương đồng G-S
						// chắc chắn là event mới từ external service --> nhét vào
						// serviceNewestEventList
						serviceNewestEventList.add(googleEvent);

						// xóa event đó đi khỏi systemEventListFromGoogle
						systemEventListFromGoogle.remove(googleEvent);
					}

					i = i - 1; // lần sau đọc outlook lại vị trí này. do đã xóa
				}

			}

			// duyệt từng outlook event
			for (int i = 0; i < systemEventListFromOutlook.size(); i++) {
				Event outlookEvent = systemEventListFromOutlook.get(i);

				boolean nextForSystemList = true;
				if (outlookEvent != null) {
					// duyệt từng system event để xem có tồn tại hay ko
					for (int j = 0; j < systemEventListFromDB.size() && nextForSystemList; j++) {
						Event systemEvent = systemEventListFromDB.get(j);

						if (systemEvent != null) {
							// nếu có tồn tại cặp O-S
							if (outlookEvent.getOutlookEventId().equals(systemEvent.getOutlookEventId())) {
								// xét tip coi có tồn tại cặp G-S ko
								// trường hợp chắc chắn đã xảy ra rồi, nên ko thể tìm dc trong đây
								// trường hợp này chỉ xảy ra khi ko có đăng ký gmail

								// duyệt google event --> do ko có đăng ký gmail nên vòng for sẽ ko xảy ra
								boolean nextForGoogleList = true;
								for (int z = 0; z < systemEventListFromGoogle.size() && nextForGoogleList; z++) {
									// size chắc chắn bằng 0
									System.out
											.println("Sync: system Google size = " + systemEventListFromGoogle.size());
									Event googleEvent = systemEventListFromGoogle.get(z);

									if (googleEvent != null) {
										// nếu tồn tại cặp G-S
										if (systemEvent.getGoogleEventId().equals(googleEvent.getGoogleEventId())) {
											// đó chính là event G-S-O cần update

											// phải xác định là cancelled hay là update--> ưu tiên
											// cancelled trước
											// cancelled là trạng thái event trên hệ thống (người
											// dùng muốn delete)
											if (systemEvent.getStatus().toLowerCase().equals("cancelled")) {
												// nhét vào updatedEventList
												updatedEventList.add(systemEvent);
											} else {
												// thực hiện so sánh xem cái nào là cái mới nhất để
												// update
												Event temp = EventBO.getNewestEventBetweenPairOfSystemEvent(
														systemEvent, googleEvent);
												Event updatedEvent = EventBO.getNewestEventBetweenPairOfSystemEvent(
														temp, outlookEvent);
												// nhét vào updatedEventList
												updatedEventList.add(updatedEvent);
											}

											// xóa event tương ứng trong google list, system list và
											// outlook list
											systemEventListFromGoogle.remove(z);
											systemEventListFromDB.remove(j);
											systemEventListFromOutlook.remove(i);

											// ngưng tìm kiếm trong system và outlook dựa trên
											// google event này (đã xử lý)
											nextForGoogleList = false;
											nextForSystemList = false;
										}
									}
									// nếu ko tồn tại thì lấy google tip theo cho tới hết list thì
									// thôi
								}

								// thoát khỏi vòng lặp tìm google --> nextForGoogleList = true
								// tức là chưa tìm ra --> ko có trong list
								// loại event 0111. Đưa vào outlookAndSystemEventList
								
								// chú ý: có thể user chỉ mới đăng ký 1 acc outlook, chưa có gmail
								// chỉ thực hiện phân loại googleAndSystemEventList khi và chỉ khi
								// có email gmail. Nếu ko có email gmail--> dạng update
								if (googleEmailList.size() != 0){
									// tức là có đăng ký email gmail
									if (nextForGoogleList) {
										// đưa vào outlookAndSystemEventList
										Event temp = EventBO.getNewestEventBetweenPairOfSystemEvent(systemEvent,
												outlookEvent);
										if (temp != null) {
											outlookAndSystemEventList.add(temp);
										} else {
											outlookAndSystemEventList.add(systemEvent);
										}
										// đưa vào thì phải xóa đi trong systemEventListFromOutlook,
										// systemEventListFromDB
										systemEventListFromOutlook.remove(i);
										systemEventListFromDB.remove(j);

										// ngừng tìm kiếm system
										nextForSystemList = false;
									}
								} else {
									// nếu ko đăng ký gmail thì xem nó là update event
									
									// phải xác định là cancelled hay là update--> ưu tiên
									// cancelled trước
									// cancelled là trạng thái event trên hệ thống (người
									// dùng muốn delete)
									if (systemEvent.getStatus().toLowerCase().equals("cancelled")) {
										// nhét vào updatedEventList
										updatedEventList.add(systemEvent);
									} else {
										// thực hiện so sánh xem cái nào là cái mới nhất để
										// update
										Event updatedEvent = EventBO.getNewestEventBetweenPairOfSystemEvent(
												systemEvent, outlookEvent);
										// nhét vào updatedEventList
										updatedEventList.add(updatedEvent);
									}

									// xóa event tương ứng trong outlook list, system list 
									systemEventListFromDB.remove(j);
									systemEventListFromOutlook.remove(i);

									// ngưng tìm kiếm trong system và google dựa trên
									// outlook event này (đã xử lý)
									nextForSystemList = false;
								}
								
							}
						}

						// vẫn chưa tìm dc system event tương ứng thì tìm tới hết list thì thôi
					}

					if (nextForSystemList) {
						// đã duyệt hết system list mà vẫn ko thấy tương đồng O-S -->
						// chắc chắn là event mới từ external service --> nhét vào
						// serviceNewestEventList
						serviceNewestEventList.add(outlookEvent);

						// xóa event đó đi khỏi systemEventListFromGoogle
						systemEventListFromOutlook.remove(outlookEvent);
					}

					i = i - 1; // lần sau đọc outlook lại vị trí này. do đã xóa
				}
			}

			// systemEventListFromDB còn lại chưa chắc là những event mới trong hệ thống
			/**
			 * TH1: chỉ đăng ký gmail. Tức là trở về dạng so sánh còn hay mất giữa sys và external
			 * Th2: chỉ đăng ký outlook. Giống TH1. chưa phải systemNewestEventList
			 * # Nếu như event system đó ko có external ID thì nó là systemNewestEventList
			 * # nếu như event ở system mà có externale ID thì nó là dạng delete (có ở sys mà ko có external)
			 * Th3: Đăng ký cả 2: Tức là việc phân loại bênn trên đã đầy đủ. 
			 * Nhưng vẫn còn 1 trường hợp. Có khi event mới tạo ra đã bị cancel hoặc 
			 * Khi event đã được follow, đã dc sync trước mà bị cancel thì chắc chắn nó ko dc sync nữa.
			 * Tức là điều kiện của nó lúc đó chính là: alone, status= cancelled. 
			 * Vậy nên phải xét trường hợp event còn lại có status = cancelled hay ko.
			 * Nếu ko ức là lúc đó là systemNewestEventList
			 * Nếu có ức là lúc đó là event phải dc xóa.
			 * 
			 * ----
			 * Nhưng khi xóa phải chú ý: event đó có là public, nếu public thì xét follow,
			 * phải xem có bao nhiêu người khác đang follow, nếu số lượng follow > 1--> ko xóa.
			 * nếu số lượng =1 tức là chỉ có chủ của nó follow thì --> xóa luôn.
			 * nếu private thì xóa luôn
			 * Th4: không Đăng ký: cái này ko nói.
			 * 
			 * Vậy phải chia 3 trường hợp như trên
			 */
			if (googleEmailList.size() != 0 && outlookEmailList.size() != 0){
				// tức là đăng ký cả 2
				FollowingBO followingBO = new FollowingBO();
				
				
				for (int i = 0; i < systemEventListFromDB.size(); i++){
					Event systemEvent = systemEventListFromDB.get(i);
					// xét có phải cancelled ko
					if (systemEvent.getStatus().equals("cancelled")){
						// là cancelled
						// thuc hiện nhiệm vụ xóa
						eventBO.deleteEvent(systemEvent, account);
					} else {
						// ko là cancelled thì tức là đang confirmed
						
						// phải xét trường hợp nó đã được sync hay chưa
						// bằng cách xem có google calendar id và outlook calendar id chưa
						
						if (systemEvent.getGcalendarId() != null && systemEvent.getOcalendarId() != null){
							// tức là đã được sync mà ở external đã xóa
							// tức là trở thành delete event
							// thuc hiện nhiệm vụ xóa
							eventBO.deleteEvent(systemEvent, account);
							
						} else {
							// nếu chưa có gì thì tức là hàng mới
							// add vào systemNewestEventList để đem đi đồng bộ trên external
							systemNewestEventList.add(systemEvent);
						}
						
					}
				}
				
			} else if (googleEmailList.size() == 0 && outlookEmailList.size() == 0){
				// thực ra if này sẽ không bao giờ xảy ra dc
				// vì sync thì phải Đăng ký email thì mới chạy dc hàm sync
			} else {
				// dành cho 2 trường hợp đăng ký 1 trong 2
				for (int i = 0; i< systemEventListFromDB.size(); i++){
					Event systemEvent = systemEventListFromDB.get(i);
					if (systemEvent.getGoogleEventId() != null || systemEvent.getOutlookEventId() != null){
						// nó là event đã đồng bộ, nhg lại alone
						// suy ra đã bị del trên google/outlook
						// vậy nên xóa luôn
						
						eventBO.deleteEvent(systemEvent, account);
						
					} else {
						systemNewestEventList.add(systemEvent);
					}
				}
				
			}
			

			// ----------- xử lý data sau khi phân loại -- insert update delete trên external
			/*
			 * systemNewestEventList dùng để lưu event dc tạo mới trên system serviceNewestEventList
			 * dùng để lưu event dc tạo mới trên service googleAndSystemEventList dùng để lưu event
			 * cần được xóa hay tạo lại outlookAndSystemEventList dùng để lưu event cần được update
			 * updatedEventList dùng để lưu event cần được update --> phải xđ là cancel hay updated
			 */
			// xử lý updatedEventList
			for (int i = 0; i < updatedEventList.size(); i++) {
				// lấy event
				Event event = updatedEventList.get(i);

				// kiểm tra status
				if (event.getStatus().toLowerCase().equals("cancelled")) {
					// thực hiện delete trên google (email lấy từ googleEmailList)
					for (int j = 0; j < googleEmailList.size(); j++) {
						Email email = googleEmailList.get(j);
						GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());
						api.deleteEvent(email.getEpcsCalendarID(), event.getGoogleEventId());
					}

					// thực hiện delete trên outlook (email lấy từ outlookEmailList)
					for (int j = 0; j < outlookEmailList.size(); j++) {
						Email email = outlookEmailList.get(j);
						OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
						api.deleteEvent(event.getOutlookEventId());
					}

					// set dirty = true
					event.setDirty(true);
					eventBO.deleteEvent(event, account);
					

				} else {
					// update trên external
					// thực hiện update trên google (email lấy từ googleEmailList)
					// NOTE: event là system event --> chuyển thành GoogleEvent
					for (int j = 0; j < googleEmailList.size(); j++) {
						Email email = googleEmailList.get(j);
						GoogleEvent googleEvent = convertToGoogleEvent(event);
						GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());
						googleEvent = api.updateEvent(googleEvent);
					}

					// thực hiện update trên outlook (email lấy từ outlookEmailList)
					// NOTE: event là system event --> chuyển thành OutlookEvent
					for (int j = 0; j < outlookEmailList.size(); j++) {
						Email email = outlookEmailList.get(j);
						OutlookEvent outlookEvent = convertToOutlookEvent(event);
						OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
						outlookEvent = api.updateEvent(outlookEvent);
					}

					// update thời gian updated_time của event trên db
					String currentDateTime = EpcsDateTimeUtil.getCurrentIsoDateTime();
					event.setUpdated(currentDateTime);
					// set dirty = true
					event.setDirty(true);
					eventBO.updateEvent(event);
				}
			}

			// xóa dư thừa trên outlook và google ---------- WARNING
			for (int i = 0; i < outlookAndSystemEventList.size(); i++) {
				// lấy event
				Event event = outlookAndSystemEventList.get(i);

				// thực hiện delete trên outlook (email lấy từ outlookEmailList)
				for (int j = 0; j < outlookEmailList.size(); j++) {
					Email email = outlookEmailList.get(j);
					OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
					api.deleteEvent(event.getOutlookEventId());
				}

				// xóa trên db
				eventBO.deleteEvent(event, account);
				
			}

			for (int i = 0; i < googleAndSystemEventList.size(); i++) {
				// lấy event
				Event event = googleAndSystemEventList.get(i);

				// thực hiện delete trên google (email lấy từ googleEmailList)
				for (int j = 0; j < googleEmailList.size(); j++) {
					Email email = googleEmailList.get(j);
					GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());
					api.deleteEvent(email.getEpcsCalendarID(), event.getGoogleEventId());
				}

				eventBO.deleteEvent(event, account);
				
			}
			// KẾT THÚC! xóa dư thừa trên outlook và google ---------- WARNING

			// đồng bộ event mới (từ external service) --> tạo mới ở các nơi và update lại ở service
			// chủ

			// ---------------------chuyển từ dạng system sang dạng google -----------//
			List<Event> systemGoogleEventList = new ArrayList<Event>();
			List<Event> systemOutlookEventList = new ArrayList<Event>();
			// phân loại
			for (int i = 0; i < serviceNewestEventList.size(); i++) {
				Event event = serviceNewestEventList.get(i);
				// set dirty = true
				event.setDirty(true);
				
				if (event.getGoogleEventId() != null) {
					// là google event
					systemGoogleEventList.add(event);
				} else {
					// là outlook event
					systemOutlookEventList.add(event);
				}
			}
			// -------------------Thực hiện trao đổi data outlook với google-----------//
			commitNewOutlookEventsToGoogleServices(systemOutlookEventList, googleEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng google -----------//

			// -------------- chuyển từ dạng system sang dạng outlook -----------//
			// --------------Thực hiện trao đổi data google với outlook-----------//
			commitNewGoogleEventsToOutlookServices(systemGoogleEventList, outlookEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng outlook -----------//

			// sau khi trao đổi data giữa các server, update lại data cho server chủ
			// --------Google-------//
			for (int i = 0; i < googleEmailList.size(); i++) {
				Email email = googleEmailList.get(i);
				// thiết lập api
				GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());

				for (int j = 0; j < systemGoogleEventList.size(); j++) {

					commitAgainToOwnerService(j, systemGoogleEventList, api);
				}
			}

			// --------Outlook-------//
			for (int i = 0; i < outlookEmailList.size(); i++) {
				Email email = outlookEmailList.get(i);
				// thiết lập api
				OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());

				for (int j = 0; j < systemOutlookEventList.size(); j++) {

					commitAgainToOwnerService(j, systemOutlookEventList, api);
				}
			}

			// ----------Cuối cùng là GHI VAO DB --------//
			
			Set<Event> eventSet = new HashSet<Event>();
			eventSet.addAll(systemGoogleEventList);
			eventSet.addAll(systemOutlookEventList);

			String last_sync = EpcsDateTimeUtil.getCurrentIsoDateTime();
			account.setEvents(eventSet);
			account.setLastupdated(last_sync);
			UserDAO userDAO = new UserDAO();
			userDAO.updateAccount(account);
			
			// tạo following cho các event đó
			FollowingBO followingBO = new FollowingBO();
			for (int i = 0; i < systemGoogleEventList.size(); i++) {
				Event event = systemGoogleEventList.get(i);
				followingBO.followingEventOrCategory(event, account);
			}

			for (int i = 0; i < systemOutlookEventList.size(); i++) {
				Event event = systemOutlookEventList.get(i);
				followingBO.followingEventOrCategory(event, account);
			}
			// Kết Thúc --- đồng bộ event mới (từ external service)

			// đồng bộ event mới (từ system) --> tạo mới ở các nơi và update lại ở service chủ

			// ---------------------chuyển từ dạng system sang dạng google -----------//
			// -------------------Thực hiện trao đổi data outlook với google-----------//
			commitNewOutlookEventsToGoogleServices(systemNewestEventList, googleEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng google -----------//

			// -------------- chuyển từ dạng system sang dạng outlook -----------//
			// --------------Thực hiện trao đổi data google với outlook-----------//
			commitNewGoogleEventsToOutlookServices(systemNewestEventList, outlookEmailList, apiMap);
			// -------------- kết thúc chuyển từ dạng system sang dạng outlook -----------//

			// sau khi trao đổi data giữa các server, update lại data cho server chủ

			// ----------Cuối cùng là GHI VAO DB --------//

			for (int i = 0; i < systemNewestEventList.size(); i++) {
				Event event = systemNewestEventList.get(i);

				eventBO.updateEvent(event);
			}

			last_sync = EpcsDateTimeUtil.getCurrentIsoDateTime();
			account.setLastupdated(last_sync);
			userDAO.updateAccount(account);
			// ---------- Kết thúc GHI VAO DB --------/
			// Kết Thúc --- đồng bộ event mới (từ external service)

			return true;
		} catch (Exception ex) {
			System.out.println("ERROR: " + ex.getMessage());
			ex.printStackTrace();
		}
		// --------------------- Kết Quả Trả Về nếu Lỗi--------------------//
		return false;
	}

	/**
	 * 
	 * @param account
	 * @param chooseCalendar
	 * @return
	 */
	public Map<String, Map<Email, List<String>>> objectReconstruction(Account account, String[] chooseCalendar) {

		List<String> googleCalendarList = new ArrayList<String>();
		List<String> outlookCalendarList = new ArrayList<String>();

		if (chooseCalendar != null) {

			// ------------ phân loại theo service
			for (int i = 0; i < chooseCalendar.length; i++) {
				if (chooseCalendar[i] != null){
					String[] part = chooseCalendar[i].split("\\|");

					// dùng part[0] để xác định loại service
					// dùng part[1] để lấy email
					// dùng part[2] để lấy calendarID

					// sosánh xem nó là google service hay outlook service
					if (part[0].equals("GOOGLE_CALENDAR")) {
						googleCalendarList.add(chooseCalendar[i].substring(part[0].length() + 1));
					} else if (part[0].equals("OUTLOOK_CALENDAR")) {
						outlookCalendarList.add(chooseCalendar[i].substring(part[0].length() + 1));
					}
				}
				
			}

			// ------------ phân loại theo email --email đang tồn tại = email user đã đăng ký
			EmailBO emailBO = new EmailBO();
			List<Email> emailList = emailBO.getAllEmail(account);

			// ----- GOOGLE_CALENDAR-----//
			Map<Email, List<String>> googleCalendarMap = new HashMap<Email, List<String>>();
			// tương ứng với mỗi email trong list
			for (int y = 0; y < emailList.size(); y++) {
				// xét một email
				Email email = emailList.get(y);
				// chỉ xử lý khi email đó là google mail
				if (email.getEmail().endsWith("@gmail.com")) {
					// list calendar tương ứng với email
					List<String> calendarList = new ArrayList<String>();

					// duyệt tất cả phần tử trong calendarList
					for (int i = 0; i < googleCalendarList.size(); i++) {
						String[] part = googleCalendarList.get(i).split("\\|");
						// dùng part0] để lấy email
						// dùng part[1] để lấy calendarID

						// sosánh xem part[0] có là email đang xét
						if (part[0].equals(email.getEmail())) {
							calendarList.add(part[1]);
						}
					}

					// duyệt xong, data cũng lấy xong, add vào map phân loại
					googleCalendarMap.put(email, calendarList);
				}
			}

			// ----- OUTLOOK_CALENDAR-----//
			Map<Email, List<String>> outlookCalendarMap = new HashMap<Email, List<String>>();
			// tương ứng với mỗi email trong list
			for (int y = 0; y < emailList.size(); y++) {
				// xét một email
				Email email = emailList.get(y);
				// chỉ xử lý khi email đó là outlook mail
				if (email.getEmail().endsWith("@outlook.com") || email.getEmail().endsWith("@hotmail.com")
						|| email.getEmail().endsWith("@live.com")) {
					// list calendar tương ứng với email
					List<String> calendarList = new ArrayList<String>();

					// duyệt tất cả phần tử trong calendarList
					for (int i = 0; i < outlookCalendarList.size(); i++) {
						String[] part = outlookCalendarList.get(i).split("\\|");
						// dùng part0] để lấy email
						// dùng part[1] để lấy calendarID

						// sosánh xem part[0] có là email đang xét
						if (part[0].equals(email.getEmail())) {
							calendarList.add(part[1]);
						}
					}

					// duyệt xong, data cũng lấy xong, add vào map phân loại
					outlookCalendarMap.put(email, calendarList);
				}
			}

			// --------- phân loại hoàn tất ---------//
			// tạo dữ liệu đầu ra
			Map<String, Map<Email, List<String>>> outputMap = new HashMap<String, Map<Email, List<String>>>();
			
			outputMap.put("GOOGLE_CALENDAR", googleCalendarMap);
			outputMap.put("OUTLOOK_CALENDAR", outlookCalendarMap);

			return outputMap;
		}

		return null;
	}

	/**
	 * 
	 * @param systemEvent
	 * @return
	 */
	public GoogleEvent convertToGoogleEvent(Event systemEvent) {
		GoogleEvent googleEvent = new GoogleEvent();

		googleEvent.setCalendar_id(systemEvent.getGcalendarId());
		googleEvent.setId(systemEvent.getGoogleEventId());
		
		if (systemEvent.getDescription() == null || systemEvent.getDescription().trim().equals("")){
			googleEvent.setDescription("Không có mô tả!");
		} else {
			googleEvent.setDescription(systemEvent.getDescription());
		}
		
		googleEvent.setLocation(systemEvent.getLocation());
		googleEvent.setStatus(systemEvent.getStatus());
		googleEvent.setSummary(systemEvent.getTitle());
		googleEvent.setVisibility(systemEvent.getVisibility());

		GoogleDateTime start = new GoogleDateTime();
		GoogleDateTime end = new GoogleDateTime();
		if (systemEvent.getIsAllDayEvent()) {
			start.setDate(systemEvent.getStartTime().split("T")[0].toString());
			end.setDate(systemEvent.getEndTime().split("T")[0].toString());
		} else {
			start.setDateTime(systemEvent.getStartTime());
			end.setDateTime(systemEvent.getEndTime());
		}
		googleEvent.setStart(start);
		googleEvent.setEnd(end);
		googleEvent.setSequence(systemEvent.getSequence());

		// reminder
		String method = "popup";
		Integer minutes = systemEvent.getReminderTime();
		GoogleReminder googleReminder = new GoogleReminder();
		List<Overrides> overrides = new ArrayList<Overrides>();
		Overrides overrideItem = new Overrides();
		overrideItem.setMethod(method);
		overrideItem.setMinutes(minutes);
		overrides.add(overrideItem);
		googleReminder.setOverrides(overrides);
		googleEvent.setReminders(googleReminder);

		return googleEvent;
	}

	/**
	 * 
	 * @param systemEvent
	 * @return
	 */
	public OutlookEvent convertToOutlookEvent(Event systemEvent) {
		OutlookEvent outlookEvent = new OutlookEvent();

		outlookEvent.setCalendar_id(systemEvent.getOcalendarId());
		outlookEvent.setId(systemEvent.getOutlookEventId());
		
		if (systemEvent.getDescription() == null || systemEvent.getDescription().trim().equals("")){
			outlookEvent.setDescription("Không có mô tả!");
		} else {
			outlookEvent.setDescription(systemEvent.getDescription());
		}
		
		outlookEvent.setLocation(systemEvent.getLocation());
		outlookEvent.setName(systemEvent.getTitle());
		outlookEvent.setVisibility(systemEvent.getVisibility());
		outlookEvent.setStart_time(systemEvent.getStartTime());
		outlookEvent.setEnd_time(systemEvent.getEndTime());

		if (systemEvent.getIsAllDayEvent()) {
			outlookEvent.setIs_all_day_event(true);
		} else {
			outlookEvent.setIs_all_day_event(false);
		}

		// reminder
		outlookEvent.setReminder_time(systemEvent.getReminderTime().intValue());

		return outlookEvent;
	}

	/**
	 * Hàm lấy data từ server theo chế độ tự động
	 * 
	 * @param calendarID
	 * @param api
	 * @param eventList
	 *            .Chú ý phải khởi tạo eventList trước khi gọi hàm
	 */
	public void getGoogleEventList(String calendarID, GoogleAPIWrapper api, List<GoogleEvent> eventList) {
		int currentMonth = EpcsDateTimeUtil.getCurrentMothInteger();

		// lấy tất cả event theo từng thời gian (theo tháng)
		if (currentMonth <= 12) {
			// nếu vẫn còn nhỏ hơn nữa (cụ thể là nhỏ hơn 9)
			if (currentMonth <= 9) {
				// thì lấy hết event trong khoảng tháng 10 tới tháng 12
				// giá trị isCurrentYear = true; là để load trong năm nay
				eventList.addAll(api.getEventList(calendarID, "10", "12", true));
				// rồi xem nó có nhỏ hơn nữa ko, cụ thể là 6 tháng
				if (currentMonth <= 6) {
					// nếu vẫn nhỏ nữa thì lấy trong khoảng từ tháng 7
					// tới tháng 9
					eventList.addAll(api.getEventList(calendarID, "7", "9", true));
					// sau đó xét tiếp xem nhỏ hơn 3 hay ko
					if (currentMonth <= 3) {
						// nếu vẫn nhỏ hơn nữa thì lấy hết trong tháng 4
						// tới tháng 6
						eventList.addAll(api.getEventList(calendarID, "4", "6", true));
						// rồi lấy từ currentMonth cho tới tháng 3

						/*
						 * getEventList mặc định nếu toMonth = null thì sẽ tìm trong 3 tháng
						 */
						eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "3", true));
					} else { // > 3
						eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "6", true));
					}
				} else { // > 6
					eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "9", true));
				}
			} else { // > 9
				eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "12", true));
			}
		}

		if (currentMonth >= 6) {
			// nếu là giữa năm, thì tiếp tục load từ tháng 1 tới tháng 6 năm sau
			eventList.addAll(api.getEventList(calendarID, "1", null, false));// 1-3
			eventList.addAll(api.getEventList(calendarID, "4", null, false)); // 4-6
		}
	}

	public void getOutlookEventList(String calendarID, OutlookAPIWrapper api, List<OutlookEvent> eventList) {
		int currentMonth = EpcsDateTimeUtil.getCurrentMothInteger();

		// lấy tất cả event theo từng thời gian (theo tháng)
		if (currentMonth <= 12) {
			// nếu vẫn còn nhỏ hơn nữa (cụ thể là nhỏ hơn 9)
			if (currentMonth <= 9) {
				// thì lấy hết event trong khoảng tháng 10 tới tháng 12
				eventList.addAll(api.getEventList(calendarID, "10", "12", true));
				// rồi xem nó có nhỏ hơn nữa ko, cụ thể là 6 tháng
				if (currentMonth <= 6) {
					// nếu vẫn nhỏ nữa thì lấy trong khoảng từ tháng 7
					// tới tháng 9
					eventList.addAll(api.getEventList(calendarID, "7", "9", true));
					// sau đó xét tiếp xem nhỏ hơn 3 hay ko
					if (currentMonth <= 3) {
						// nếu vẫn nhỏ hơn nữa thì lấy hết trong tháng 4
						// tới tháng 6
						eventList.addAll(api.getEventList(calendarID, "4", "6", true));
						// rồi lấy từ currentMonth cho tới tháng 3

						/*
						 * getEventList mặc định nếu toMonth = null thì sẽ tìm trong 3 tháng
						 */
						eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "3", true));
					} else { // > 3
						eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "6", true));
					}
				} else { // > 6
					eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "9", true));
				}
			} else { // > 9
				eventList.addAll(api.getEventList(calendarID, String.valueOf(currentMonth), "12", true));
			}
		}
		if (currentMonth >= 6) {
			// nếu là giữa năm, thì tiếp tục load từ tháng 1 tới tháng 6 năm sau
			eventList.addAll(api.getEventList(calendarID, "1", null, false));// 1-3
			eventList.addAll(api.getEventList(calendarID, "4", null, false)); // 4-6
		}
	}

	public Event convertFromGoogleEventToSystemEvent(Account account, GoogleEvent googleEvent) {
		Event systemEvent = new Event();

		systemEvent.setAccount(account);
		systemEvent.setGoogleEventId(googleEvent.getId());
		systemEvent.setGcalendarId(googleEvent.getCalendar_id());
		systemEvent.setTitle(googleEvent.getSummary());
		/*
		 * google visibility is very khủng gồm 3 cái: default, public, private vậy default là gì, ko
		 * bit vậy cứ xem default là null, public là public còn private là private
		 * 
		 * trong hệ thống default là public --> nếu gặp null thì chuyển thành public
		 */
		if (googleEvent.getVisibility() == null){
			systemEvent.setVisibility("public");
		} else {
			systemEvent.setVisibility(googleEvent.getVisibility());
		}
		
		if (googleEvent.getStart().getDate() != null) {// allday event
														// yyyy-MM-dd
			String startDateTime = googleEvent.getStart().getDate() + "T00:00:00+07:00";
			String endDateTime = googleEvent.getEnd().getDate() + "T00:00:00+07:00";

			systemEvent.setIsAllDayEvent(true);
			systemEvent.setStartTime(startDateTime);
			systemEvent.setEndTime(endDateTime);
		} else {
			systemEvent.setIsAllDayEvent(false);
			systemEvent.setStartTime(googleEvent.getStart().getDateTime());
			systemEvent.setEndTime(googleEvent.getEnd().getDateTime());
		}

		if (googleEvent.getDescription() == null || googleEvent.getDescription().trim().equals("")){
			systemEvent.setDescription("Không có mô tả!");
		} else {
			systemEvent.setDescription(googleEvent.getDescription());
		}
		
		systemEvent.setDirty(false);
		systemEvent.setSequence(googleEvent.getSequence());
		// chưa hỗ trợ recurrent
		systemEvent.setIsRecurrent(false);
		systemEvent.setLocation(googleEvent.getLocation());
		systemEvent.setStatus(googleEvent.getStatus());
		// SAI systemEvent.setOcalendarId(email.getEpcsCalendarID());
		systemEvent.setOrganizer(null);
		systemEvent.setOriginalEvent(null);
		systemEvent.setIsNotified(false);
		
		// lấy reminder từ google
		GoogleReminder googleReminder = googleEvent.getReminders();
		Integer minutes = 15;
		if (googleReminder != null) {
			if (!googleReminder.getUseDefault()) {
				// String method = "popup";
				// get overrides
				if (googleReminder.getOverrides() != null) {
					Overrides overrides = googleReminder.getOverrides().get(0);
					minutes = overrides.getMinutes();
				}
			}
		}
		systemEvent.setReminderTime(minutes);

		systemEvent.setUpdated(googleEvent.getUpdated());

		return systemEvent;
	}

	public Event convertFromOutlookEventToSystemEvent(Account account, OutlookEvent outlookEvent) {
		Event systemEvent = new Event();

		systemEvent.setAccount(account);
		systemEvent.setOutlookEventId(outlookEvent.getId());
		systemEvent.setOcalendarId(outlookEvent.getCalendar_id());
		systemEvent.setTitle(outlookEvent.getName());
		systemEvent.setVisibility(outlookEvent.getVisibility());
		systemEvent.setIsAllDayEvent(outlookEvent.getIs_all_day_event());

		String startTime = EpcsDateTimeUtil.convertToSystemDatetime(outlookEvent.getStart_time());
		systemEvent.setStartTime(startTime);

		String endTime = EpcsDateTimeUtil.convertToSystemDatetime(outlookEvent.getEnd_time());
		systemEvent.setEndTime(endTime);

		if (outlookEvent.getDescription() == null || outlookEvent.getDescription().trim().equals("")){
			systemEvent.setDescription("Không có mô tả!");
		} else {
			systemEvent.setDescription(outlookEvent.getDescription());
		}

		systemEvent.setDirty(false);
		// từ outlook xuống chưa có sequence nào
		systemEvent.setSequence(0);
		// chưa hỗ trợ recurrent
		systemEvent.setIsRecurrent(false);
		systemEvent.setLocation(outlookEvent.getLocation());
		// outlook ko có status --> mặc định là confirmed/ cancelled
		systemEvent.setStatus("confirmed");
		// SAI systemEvent.setGcalendarId(email.getEpcsCalendarID());
		systemEvent.setOrganizer(null);
		systemEvent.setOriginalEvent(null);
		systemEvent.setIsNotified(false);

		// lấy reminder từ outlook
		systemEvent.setReminderTime(outlookEvent.getReminder_time());

		systemEvent.setUpdated(outlookEvent.getUpdated_time());
		return systemEvent;
	}

	public void commitNewOutlookEventsToGoogleServices(List<Event> systemOutlookEventList, List<Email> googleEmailList,
			Map<String, Object> apiMap) {

		if (systemOutlookEventList.size() > 0) {
			// với từng account google, đưa hết event lên và lưu trong EPCS calendar của google
			for (int index = 0; index < googleEmailList.size(); index++) {
				Email email = googleEmailList.get(index);

				// thiết lập api
				GoogleAPIWrapper api = (GoogleAPIWrapper) apiMap.get(email.getEmail());
				// với từng event system, tạo 1 event google, nhét calendarID của epcs vào
				for (int i = 0; i < systemOutlookEventList.size(); i++) {
					Event systemEvent = systemOutlookEventList.get(i);
					
					// kiểm tra system event đã có google event id chưa
					// nếu có tức là ko phải new event từ outlook mà chính là event đã sync rồi.
					if (systemEvent.getGoogleEventId() == null) {
						// lúc tách từ outlook sang system chưa có lấy acc google
						// do phải sử dụng account google thì mới có epcs của google
						// bây giờ đã có
						// set tiếp gCalendarID cho systemEvent lấy từ google
						systemEvent.setGcalendarId(email.getEpcsCalendarID());

						GoogleEvent googleEvent = convertToGoogleEvent(systemEvent);

						googleEvent = api.insertEvent(googleEvent);

						if (googleEvent != null) {
							systemEvent.setGoogleEventId(googleEvent.getId());
						}
					}
					
				}
			}
		}
	}

	public void commitNewGoogleEventsToOutlookServices(List<Event> systemGoogleEventList, List<Email> outlookEmailList,
			Map<String, Object> apiMap) {

		if (systemGoogleEventList.size() > 0) {
			// với từng account outlook, đưa hết event lên và lưu trong EPCS calendar của
			// outlook
			for (int index = 0; index < outlookEmailList.size(); index++) {
				Email email = outlookEmailList.get(index);

				// thiết lập api
				OutlookAPIWrapper api = (OutlookAPIWrapper) apiMap.get(email.getEmail());
				// với từng event system, tạo 1 event outlook, nhét calendarID của epcs vào
				for (int i = 0; i < systemGoogleEventList.size(); i++) {
					Event systemEvent = systemGoogleEventList.get(i);
					
					// kiểm tra system event đã có google event id chưa
					// nếu có tức là ko phải new event từ google mà chính là event đã sync rồi.
					if (systemEvent.getOutlookEventId() == null) {
						// lúc tách từ google sang system chưa có lấy acc outlook
						// do phải sử dụng account outlook thì mới có epcs của outlook
						// bây giờ đã có
						// set tiếp gCalendarID cho systemEvent lấy từ outlook
						systemEvent.setOcalendarId(email.getEpcsCalendarID());

						OutlookEvent outlookEvent = convertToOutlookEvent(systemEvent);

						outlookEvent = api.insertEvent(outlookEvent);

						if (outlookEvent != null) {
							systemEvent.setOutlookEventId(outlookEvent.getId());
						}
					}
					
				}

			}
		}
	}

	public void commitAgainToOwnerService(int index, List<Event> newestExternalEventList, Object api) {

		if (api instanceof GoogleAPIWrapper) {
			GoogleAPIWrapper googleApi = (GoogleAPIWrapper) api;
			Event systemEvent = newestExternalEventList.get(index);
			systemEvent.setSequence(systemEvent.getSequence() + 1);

			GoogleEvent googleEvent = convertToGoogleEvent(systemEvent);
			googleEvent = googleApi.updateEvent(googleEvent);

			// set lại last updated
			String last_updated = EpcsDateTimeUtil.convertToSystemDatetime(googleEvent.getUpdated());
			systemEvent.setUpdated(last_updated);
		} else {
			OutlookAPIWrapper outlookApi = (OutlookAPIWrapper) api;
			Event systemEvent = newestExternalEventList.get(index);
			// update outlook ko cần tăng sequence
			// systemEvent.setSequence(systemEvent.getSequence() + 1);

			OutlookEvent outlookEvent = convertToOutlookEvent(systemEvent);
			outlookEvent = outlookApi.updateEvent(outlookEvent);

			// set lại last updated
			// NOTE: sau khi update thi chưa có updated_time ở outlook, phải get event lại
			// mới có
			outlookEvent = outlookApi.getEvent(outlookEvent.getId());
			String last_updated = EpcsDateTimeUtil.convertToSystemDatetime(outlookEvent.getUpdated_time());
			systemEvent.setUpdated(last_updated);
		}

	}

	/**
	 * Nguyên tắc xử lý: Android chỉ có dirty, delete, status, syncID (tức eventID trên hệ thống).
	 * System có: dirty, status, eventID.
	 * 
	 * vậy nên: khi system và android có dirty = 1 thì ưu tiên cho system. khi update cho system
	 * thì: dirty=1, status=confirmed, delete=0, có syncID, và system thì dirty=0. khi delete cho
	 * system thì: dirty=1, status=cancelled, có syncID, delete=1 (thường ko cần), và system thì
	 * dirty=0. khi insert cho system thì: dirty=1, status=confirmed, chưa có syncID
	 * 
	 * update và delete thì ưu tiên delete, dù ở bất kỳ đâu cũng ưu tiên delete
	 * 
	 * Đăc biệt: android (dạng update) còn mà system mất rồi thì là chuyển thành dạng delete--> để
	 * trả về cho android delete
	 * 
	 * Chú ý: delete từ android lên system tức là chuyển status systemEvent thành cancelled .Vì
	 * delete tức là mất vết trên service --> error
	 * 
	 * --------------------Thuật giải ---------------- 1. tách add list từ android (ko có syncID) 2.
	 * tách delete list từ android list (cancelled --> xóa trong list) 3. duyệt tìm systemEvent là
	 * cancelled tương ứng với delete list (xóa trong list) 4. tách delete từ system list
	 * (cancelled, dirty = 1 --> xóa trong list) 5. duyệt tìm android list (xóa trong list) 6. tách
	 * update list và addToAndroidList cho cả 2 list còn lại của system và android --> cách tách:
	 * duyệt theo cặp. Với mỗi item trong android list duyệt system list --> tìm xem có eventID
	 * trùng ko. --> Nếu trùng xét dirty = 1 ko, --------->(nếu bằng thì so sánh, nhét vào update
	 * list, xóa) --------->(nếu ko bằng thì xóa --> ko cần update) --> nếu ko trùng (trường hợp khi
	 * có syncID mà mất trên system) --> đưa vào delete 7. Còn lại trên system chính là addToAndroid
	 * 
	 * Để ngăn tình trạng mất vết event tạo sẵn ở android (ex: ở android có 2 event: Id1-sync
	 * null-status conf, Id2-sync null-status conf) thì phải tạo map để lưu lại dc vết của Id1 và
	 * Id2 để còn updated cho 2 event đó
	 * 
	 * 
	 * 
	 * @param androidEventList
	 * @param username
	 */
	public synchronized AndroidEventList androidSync(AndroidEventList androidEventList, String username) {
		Account account = new UserBO().getAccountByUsername(username);
		EventBO eventBO = new EventBO();
		// phân loại các event trong systemListFromAndroidList thành add, update, delete
		List<Event> addToSystemList = new ArrayList<Event>();
		List<Event> updatedList = new ArrayList<Event>();
		List<Event> deletedList = new ArrayList<Event>();
		// phân loại các event trong systemListFromDB thành add, update, delete
		List<Event> addToAndroidList = new ArrayList<Event>();
		// dùng để chuyen từ android list về system list
		List<Event> systemListFromAndroidList = null;
		// lấy system event list từ db lên để so sánh
		List<Event> systemListFromDB = eventBO.getAllEventForSync(account);

		// -----------------Bắt đầu phân loại-----------------//
		Map<AndroidEvent, Event> traceMap = new HashMap<AndroidEvent, Event>();
		// -----------------Xử lý data từ android -------//
		if (androidEventList != null) {

			// chuyen từ android list về system list
			systemListFromAndroidList = new ArrayList<Event>();
			for (int i = 0; i < androidEventList.size(); i++) {
				AndroidEvent androidEvent = androidEventList.get(i);
				Event event = eventBO.convertAndroiEventToSystemEvent(androidEvent);

				systemListFromAndroidList.add(event);
				traceMap.put(androidEvent, event);
			}

			// ------Bắt đầu phân addToSystem và deleteList
			// (chưa có syncID --> tức là chưa có ID ở systemList) ----//
			// --- data nằm trên systemListFromAndroidList
			for (int i = 0; i < systemListFromAndroidList.size(); i++) {
				Event event = systemListFromAndroidList.get(i);
				if (event.getEventId() == null) {
					// chuyển trạng thái về đã updated
					event.setDirty(Boolean.valueOf(false));

					// khởi tạo cho object mới
					event.setAccount(account);
					event.setSequence(0);
					// set last updated
					event.setUpdated(EpcsDateTimeUtil.getCurrentIsoDateTime());

					// add vào list
					addToSystemList.add(event);

					// xóa trong systemListFromAndroidList
					systemListFromAndroidList.remove(i);

					// back lại 1 dơn vị
					i = i - 1;
				} else {
					// phân loại delete (tức là status = cancelled)
					if (event.getStatus().toLowerCase().equals("cancelled")) {
						event.setDirty(Boolean.valueOf(false));

						deletedList.add(event);

						// xóa trong systemListFromAndroidList
						systemListFromAndroidList.remove(i);

						// back lại 1 dơn vị
						i = i - 1;
					}
				}
			}
			// ------Kết thúc phân addToSystem và deleteList
		}

		// duyệt tìm các delete event (trong systemListFromDB) tương ứng với delList
		for (int i = 0; i < deletedList.size(); i++) {
			Event deletedEvent = deletedList.get(i);
			for (int j = 0; j < systemListFromDB.size(); j++) {
				Event event = systemListFromDB.get(j);

				// nếu đúng là event cần tìm
				if (event.getEventId().compareTo(deletedEvent.getEventId()) == 0) {

					// khôi phục data cho object cần delete (ko là mất vết external ID)
					deletedEvent.setAccount(event.getAccount());
					deletedEvent.setComments(event.getComments());
					deletedEvent.setEndTime(event.getEndTime());
					deletedEvent.setStartTime(event.getStartTime());
					deletedEvent.setEventcategory(event.getEventcategory());
					deletedEvent.setEvents(event.getEvents());
					deletedEvent.setGcalendarId(event.getGcalendarId());
					deletedEvent.setGoogleEventId(event.getGoogleEventId());
					deletedEvent.setIsAllDayEvent(event.getIsAllDayEvent());
					deletedEvent.setIsRecurrent(event.getIsRecurrent());
					deletedEvent.setLocation(event.getLocation());
					deletedEvent.setOcalendarId(event.getOcalendarId());
					deletedEvent.setOrganizer(event.getOrganizer());
					deletedEvent.setOriginalEvent(event.getOriginalEvent());
					deletedEvent.setOutlookEventId(event.getOutlookEventId());
					deletedEvent.setRecurrence(event.getRecurrence());
					// do giá trị android cung cấp là -1 --> phải lấy lại giá trị cho nó đẹp
					deletedEvent.setReminderTime(event.getReminderTime());
					deletedEvent.setSequence(event.getSequence());
					deletedEvent.setTitle(event.getTitle());
					deletedEvent.setVisibility(event.getVisibility());

					// xóa khỏi systemListFromDB
					systemListFromDB.remove(j);

					// quay về vị trí trước đó để đọc lại
					j = j - 1;
				}
			}
		}

		// phân loại delete cho systemListFromDB (cancelled và dirty = 1)
		/**
		 * Chú ý: delete của system phải là dạng update cho event android có sẵn. Vì theo flow: khi
		 * android có va 2muốn delete trên system thì kết quủa là trên android sẽ bị delete nhưng
		 * trên system thì còn tồn tại và change status thành cancelled. Vì vậy lần sync tiếp theo
		 * sẽ vẫn thấy event system đó.
		 * 
		 * Nhưng nó đã ở dạng cancelled --> vì vậy nếu tồn tại 1 cặp A-S với dạng delete thì đó mới
		 * dc xem là delete dạng update. Còn ko (chỉ tồn tại ở system thì nó ko dc sync nữa.
		 * 
		 * Tức là ta cứ xét nó là dạng delete trước. Sau đó xét xem có trong android List tương ứng
		 * ko. nếu có thì xử lý xóa đi trong androidList. Nếu ko thì xóa trong delete List của hệ
		 * thống.
		 */
		for (int i = 0; i < systemListFromDB.size(); i++) {
			Event event = systemListFromDB.get(i);
			if (event.getStatus().equals("cancelled")) {

				if (event.getDirty().booleanValue() == true) {
					event.setDirty(Boolean.valueOf(false));

					// added------
					boolean noNeedToSync = true;
					for (int j = 0; j < systemListFromAndroidList.size() && noNeedToSync; j++) {
						Event androidEvent = systemListFromAndroidList.get(j);

						// nếu thấy cặp A-S event
						if (event.getEventId().compareTo(androidEvent.getEventId()) == 0) {
							// trươc khi xoa gán giá trị cho event ben android
							
							//-----error here--- 
							// gán biến mới cho status --> nhằm chuyển DELETE android sang 1
							androidEvent.setStatus("cancelled");
							androidEvent.setDirty(false);
							
							systemListFromAndroidList.remove(j);

							// xác nhận đây là event delete dạng update
							noNeedToSync = false;
							j = j - 1;
						}
					}

					if (!noNeedToSync) {
						// nếu là event delete dang update (needToSync) thì
						deletedList.add(event);
					}

				}

				// xóa trong systemListFromDB
				systemListFromDB.remove(i);

				// back lại 1 đơn vị
				i = i - 1;

			}
		}

		// --------------------- Kết thúc quá trình phân loại add, delete, và phần còn lại
		/**
		 * chúng ta có 1 list addToSystem, 1 list delete, 1 list android, 1 list system. Chú ý:
		 * android list có thể tồn tại cả update item và nochange item. system list có thể tồn tại
		 * update item, nochange item, và add item.
		 * 
		 * --> vậy nên phải tách theo update item để lòi ra add item trong system list. --> phải
		 * dùng list ít loại nhất (list android) để duyệt vòng thứ nhất. Chú ý cuối cùng: list
		 * android sẽ có trường hợp có item nhg ko tìm ra bên sysList --> delete
		 */

		if (androidEventList != null) {
			for (int i = 0; i < systemListFromAndroidList.size(); i++) {
				Event androidEvent = systemListFromAndroidList.get(i);

				// đánh dấu androidEvent cần bị delete (khi ko tìm ra systemEvent tương ứng)
				boolean isDeletedItem = true;
				boolean getNext = true;

				for (int j = 0; j < systemListFromDB.size() && getNext; j++) {
					Event systemEvent = systemListFromDB.get(j);

					// tìm ra tức là trùng nhau EventID
					if (androidEvent.getEventId().compareTo(systemEvent.getEventId()) == 0) {

						Event updatedEvent = EventBO.getNewestEventBetweenPairOfSystemEventForAndroid(systemEvent,
								androidEvent);

						if (updatedEvent != null) {
							// set lại last updated
							updatedEvent.setUpdated(EpcsDateTimeUtil.getCurrentIsoDateTime());
							
							// gán android event bằng updated event -->deep copy
							// nhét thông tin mới vào system event
							androidEvent.setDescription(updatedEvent.getDescription());
							androidEvent.setIsAllDayEvent(updatedEvent.getIsAllDayEvent());
							// androidEvent.setIsRecurrent(updatedEvent.getIsRecurrent());
							androidEvent.setLocation(updatedEvent.getLocation());
							androidEvent.setVisibility(updatedEvent.getVisibility());
							androidEvent.setTitle(updatedEvent.getTitle());
							androidEvent.setStatus(updatedEvent.getStatus());
							androidEvent.setEndTime(updatedEvent.getEndTime());
							androidEvent.setStartTime(updatedEvent.getStartTime());
							androidEvent.setReminderTime(updatedEvent.getReminderTime());
							androidEvent.setDirty(Boolean.valueOf(false));
							
							
							// add vào list
							updatedList.add(updatedEvent);
						}

						// xóa trong list cũ
						systemListFromAndroidList.remove(i);
						systemListFromDB.remove(j);

						// ko cần tìm tiếp
						getNext = false;

						// set androidEvent có isDeletedItem = false
						isDeletedItem = false;
					}
				}
				// kết thúc vòng lặp tìm event item tương ứng

				if (isDeletedItem) {
					// nếu isDeletedItem = true --> event đó là updated mà ko còn data trên system
					// nhét vào delete list để đem về android delete --> do ưu tiên delete

					androidEvent.setDirty(Boolean.valueOf(false));
					androidEvent.setStatus("cancelled");
					deletedList.add(androidEvent);

					// xóa trong list cũ
					systemListFromAndroidList.remove(i);
				}

				// trước sau gì thì event cũng bị xóa --> back lại 1 đơn vị
				i = i - 1;

			}
		}

		/**
		 * chạy hết phân loại android list. nếu trong system list vẫn còn thì đó là added event nhét
		 * vào addToAndroidList
		 */
		addToAndroidList.addAll(systemListFromDB);

		// --------------------Kết thúc phân loại --------------//

		// --------------Bắt đầu cập nhật -----------------//
		EventDAO eventDAO = new EventDAO();
		FollowingBO followingBO = new FollowingBO();
		// 1. Update
		for (int i = 0; i < updatedList.size(); i++) {
			Event event = updatedList.get(i);

			/**
			 * Trường hợp: khi ko có android list (null) thì lúc đó updatedList có thể chứa cả
			 * updated lẫn added và nochange của system. Vì vậy updated_time sẽ ko dc update trong
			 * vòng for này mà phải dc update tại ngay vị trí event thực sự là updated event (ở khâu
			 * phân loại)
			 */
			eventDAO.updateEvent(event);
			System.out.println("chạy update");
		}
		// 2. delete
		for (int i = 0; i < deletedList.size(); i++) {
			Event event = deletedList.get(i);

			// set lại last updated
			event.setUpdated(EpcsDateTimeUtil.getCurrentIsoDateTime());

			eventBO.cancelEvent(event);
			System.out.println("chạy delete");
		}
		// 3. insert to db
		for (int i = 0; i < addToSystemList.size(); i++) {
			Event event = addToSystemList.get(i);

			eventDAO.addEvent(event);
			
			// tạo following cho các event đó			
			followingBO.followingEventOrCategory(event, account);
		}
		// 4. trước khi insert to android, chuyển dirty addToAndroidList thành false
		for (int i = 0; i < addToAndroidList.size(); i++) {
			Event event = addToAndroidList.get(i);
			event.setDirty(Boolean.valueOf(false));
			eventDAO.updateEvent(event);
		}
		// -------------Kết thúc cập nhật --------------//

		/*
		 * / ------------ Lấy các list parse lại thành AndroidEventList (không cần) -----//
		 * List<Event> finalSystemListForAndroid = new ArrayList<Event>();
		 * finalSystemListForAndroid.addAll(addToSystemList);
		 * finalSystemListForAndroid.addAll(updatedList);
		 * finalSystemListForAndroid.addAll(deletedList);
		 * finalSystemListForAndroid.addAll(addToAndroidList);
		 * 
		 * AndroidEventList finalAndroidEventList =
		 * eventBO.convertSystemListToAndroidEventList(finalSystemListForAndroid); // ------------
		 * Lấy các list parse lại thành AndroidEventList (không cần) -----/
		 */

		// thực hiện convert từ SystemEvent sang AndroidEvent (sao cho có tồn tại _ID ở android)
		AndroidEventList finalAndroidEventList = new AndroidEventList();
		for (int i = 0; i < androidEventList.size(); i++) {
			AndroidEvent androidEvent = androidEventList.get(i);
			Event systemEvent = traceMap.get(androidEvent);
			AndroidEvent tempAndroidEventFromSystemEvent = eventBO.convertSystemEventToAndroiEvent(systemEvent);
			tempAndroidEventFromSystemEvent.set_ID(androidEvent.get_ID());

			finalAndroidEventList.add(tempAndroidEventFromSystemEvent);
		}
		for (int i = 0; i < addToAndroidList.size(); i++) {
			Event systemEvent = addToAndroidList.get(i);
			AndroidEvent tempAndroidEventFromSystemEvent = eventBO.convertSystemEventToAndroiEvent(systemEvent);

			finalAndroidEventList.add(tempAndroidEventFromSystemEvent);
		}

		return finalAndroidEventList;

	}
}
